/*******************************************************************************
 * Copyright 2010 DemandSpot
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package com.demandspot.utils;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;


public class BitlyApi {
		
	private static final String linkRex = "(https?://[\\S]*)";
	private String urlTemplate = "";
	private XPathFactory factory = XPathFactory.newInstance();
	private XPath xpath = factory.newXPath();
	private XPathExpression keyExpr;
	private XPathExpression shortExpr;		
	private Pattern linkPatern;
	private DefaultHttpClient client = new DefaultHttpClient();

	public BitlyApi(String login, String apiKey) throws XPathExpressionException {
		if (StringUtils.isEmpty(login) || StringUtils.isEmpty(apiKey)) {
			throw new IllegalArgumentException("login and apiKey must be provided");
		}
		urlTemplate = "http://api.bit.ly/shorten?version=2.0.1&login=" + login + "&apiKey=" + apiKey + "&format=xml&history=1";
		linkPatern = Pattern.compile(linkRex);
		keyExpr = xpath.compile("//bitly/results/nodeKeyVal/nodeKey/text()");
		shortExpr = xpath.compile("//bitly/results/nodeKeyVal/shortUrl/text()");
	}
	
	public String shortenText(String text) {
		Matcher matcher = linkPatern.matcher(text);		
		List<String> matches = new ArrayList<String>();
		while (matcher.find()) {
			if (matcher.groupCount() > 0) {
				String match = matcher.group(1);
				matches.add(match);			
			}
		}
		try {
			Map<String, String> shortUrls = shorten(matches);
			for (Entry<String, String> entry : shortUrls.entrySet()) {
				text = StringUtils.replace(text, entry.getKey(), entry.getValue());
			}
		} catch (Exception e) {
			ExceptionUtils.toRuntime(e);
		}
		return text;
	}
	
	public Map<String, String> shorten(List<String> urls) throws ClientProtocolException, IOException, ParserConfigurationException, IllegalStateException, SAXException, XPathExpressionException {
		Map<String, String> shortUrls = new HashMap<String, String>();
		if (urls == null || urls.isEmpty()) {
			return shortUrls;
		}        
        StringBuilder endpoint = new StringBuilder(urlTemplate);
        for (String url : urls) {
        	endpoint.append("&longUrl=").append(URLEncoder.encode(url, "UTF-8"));
        }
        HttpGet get = new HttpGet(endpoint.toString());
        HttpResponse response = client.execute(get);
        
        if (200 == response.getStatusLine().getStatusCode()) {
        	HttpEntity entity = response.getEntity();
        	if (entity != null) {
    			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    			factory.setNamespaceAware(true); // never forget this!
    			DocumentBuilder builder = factory.newDocumentBuilder();
    			Document doc = builder.parse(new InputSource(entity.getContent()));
    			NodeList keys = (NodeList) keyExpr.evaluate(doc, XPathConstants.NODESET);   				
    			NodeList values = (NodeList) shortExpr.evaluate(doc, XPathConstants.NODESET);
    			
    			if (keys.getLength() != urls.size()) {
    				throw new RuntimeException("Invalid size from bitly map got: " + keys.getLength() + ", expected: " + urls.size());
    			}
    			
   				for (int i = 0; i < keys.getLength(); i++) {
   					try {
	   					String key = keys.item(i).getTextContent();
	   					String value = values.item(i).getTextContent();
	   					if (key != null && value != null && key.length() * value.length() > 0) {
	   						shortUrls.put(key, value);	
	   					}
   					} catch (Exception ex) {
   						ExceptionUtils.toRuntime(ex);
   					}   					
   			    }   				
        	}        		
        }
        return shortUrls;
	}
		
}
